var express = require('express');
var Excel = require("exceljs");
var router = express.Router();
var formidable = require('formidable');
const Relation = require('../models/Relation');
const RelationInstance = require('../models/RelationInstance');
const Instance = require('../models/Instance');
var tools = require('../utils/tools')
var log = require('../utils/logs').getLogger();

/**
 * @swagger
 *
 * /cmdb/relationinstances/relation/{relationId}/page:
 *   get:
 *     tags:
 *       - 关系实例管理
 *     summary: 分页查询关系实例
 *     description: 支持标准方式查询。$_foo=bar
 *     parameters:
 *       - name: relationId
 *         description: 关系ID
 *         in: path
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: 返回标准分页结构
 */
router.get('/relation/:relationId/page', async function (req, res, next) {
  var query = tools.queryResolver(req.query)
  query.rela = req.params.relationId
  RelationInstance.page(query, req.query.page, req.query.size, result => {
    return res.json({ status: 'success', data: result.docs, pageable: result.pageable })
  })
});

/**
  查询关系定义下所有的关系实例
*/
router.get('/relation/:relationId', function (req, res, next) {
  RelationInstance.find({ rela: req.params.relationId })
    .populate('src')
    .populate('dest')
    .populate('rela')
    .exec((err, docs) => {
      if (err) return tools.handleError(res, err);
      return res.json({ status: 'success', data: docs })
    })
});

/**
  查询关系定义下所有的关系实例数量
*/
router.get('/relation/:relationId/count', async function (req, res, next) {
  var count = await RelationInstance.count({ rela: req.params.relationId })
  return res.json({ status: 'success', data: count })
});

/**
  关联关系实例详情
*/
router.get('/:id', function (req, res, next) {
  RelationInstance.findOne({ _id: req.params.id })
    .populate('src')
    .populate('dest')
    .populate('rela')
    .exec((err, doc) => {
      if (err) return tools.handleError(res, err);
      return res.json({ status: 'success', data: doc })
    })
});

/**
  更新关联关系实例
  目前只允许更新desc和属性值。关联关系、源实例、目标实例不允许更新
*/
router.put('/:id', async function (req, res, next) {
  var rInstance = await RelationInstance.findById(req.params.id).populate('rela')
  if (!rInstance) {
    return tools.handleError(res, '关系不存在');
  }
  rInstance.rela.attrs
  var attrs = []
  rInstance.rela.attrs.forEach(a => {
    attrs.push({ key: a.field, value: req.body.attrs[a.field] })
  })

  var { desc } = req.body
  var data = { desc, attrs }
  RelationInstance.findByIdAndUpdate(req.params.id, { $set: data }, { new: true }, function (err, doc) {
    if (err) return tools.handleError(res, err);
    return res.json({ status: 'success', data: doc })
  });
});

/**
* 根据关系定义导入关系实例
* 目前不支持一个Excel模板导入任意关系实例
*/
router.post('/relation/:relationId/import', async function (req, res, next) {
  let form = new formidable.IncomingForm();
  form.encoding = 'utf-8';
  form.keepExtensions = true;

  form.parse(req, async (err, fields, files) => {
    if (err) return tools.handleError(res, err);

    const filePath = files.file.path;
    const workbook = new Excel.Workbook();
    await workbook.xlsx.readFile(filePath);

    var mRelation = await Relation.findById(req.params.relationId)
    if (!mRelation) {
      return tools.handleError(res, '未找到关系定义');
    }
    
    const worksheet = workbook.getWorksheet(1);
    // Excel第一行是表头，后面解析动态属性时，要根据属性名称对应查找
    const row = worksheet.getRow(1);

    // [ [列序号,列名称] ]
    const colSeq = []
    row.eachCell(function (cell, colNumber) {
      colSeq.push([colNumber, cell.value])
    });

    // 先查出关系定义的所有动态属性
    var allAttrs = [...mRelation.attrs]
    // 防止上传一些未定义的属性
    const mAttrs = allAttrs.filter(it => colSeq.some(n => it.name === n[1]))
    const attrMap = {}
    mAttrs.forEach(it => {
      attrMap[it.name] = it
    })

    // 遍历工作表中具有值的所有行
    const insts = []
    worksheet.eachRow(function (row, rowNumber) {
      if (rowNumber > 1) {
        const inst = {
          rela: mRelation._id,
          src: '',
          dest: '',
          desc: ''
        }
        // 源实例，目标实例在Excel表格中的值是code$$name结构
        const srcCol = colSeq.find(it => it[1] === '源实例')
        if (srcCol) {
          inst.src = row.getCell(srcCol[0]).value
        }
        const destCol = colSeq.find(it => it[1] === '目标实例')
        if (destCol) {
          inst.dest = row.getCell(destCol[0]).value
        }
        const descCol = colSeq.find(it => it[1] === '描述')
        if (descCol) {
          inst.desc = row.getCell(descCol[0]).value
        }

        const instAttrs = []
        colSeq.forEach(s => {
          if (attrMap[s[1]]) {
            let val = row.getCell(s[0]).value
            instAttrs.push({
              key: attrMap[s[1]].field,
              value: val
            })
          }
        })
        inst.attrs = instAttrs
        insts.push(inst)
      }
    });

    var docs = []
    for(var i=0; i<insts.length; i++) {
      // 忽略掉不合法的数据
      if(!insts[i].src) continue;
      var srcInst = await Instance.findOne({code: (insts[i].src + '').split('$$')[0]})
      // 忽略掉不合法的数据
      if(!srcInst) continue;

      // 忽略掉不合法的数据
      if(!insts[i].dest) continue;
      var destInst = await Instance.findOne({code: (insts[i].dest + '').split('$$')[0]})
      // 忽略掉不合法的数据
      if(!destInst) continue;

      var doc = {...insts[i]}
      doc.src = srcInst._id
      doc.dest = destInst._id

      var existed = await RelationInstance.count({rela: doc.rela, src: doc.src, dest: doc.dest})
      if(existed > 0) continue;
      docs.push(doc)
    }

    if(docs.length == 0) {
      return res.json({ status: 'success', data: 0 })
    }

    try {
      var result = await RelationInstance.insertMany(docs, { ordered: false, rawResult: true })
      for(var i=0; i<docs.length; i++) {
        // 更新源实例的引用
        var rSrc = result.ops.filter(item => item.src.toString() === docs[i].src.toString())
        await Instance.findByIdAndUpdate(docs[i].src, { $addToSet: { relations: rSrc } }, { new: false });

        // 更新目标实例的引用
        var rDest = result.ops.filter(item => item.dest.toString() === docs[i].dest.toString())
        await Instance.findByIdAndUpdate(docs[i].dest, { $addToSet: { relations: rDest } }, { new: false });
      }
      return res.json({ status: 'success', data: result.insertedCount })
    } catch (err) {
      log.error(err)
      return tools.handleError(res, err.writeErrors);
    }
  })
})

/**
 * @swagger
 *
 * /cmdb/relationinstances/batch:
 *   delete:
 *     tags:
 *       - 关系实例管理
 *     summary: 删除关联关系实例
 *     description: 删除关联关系实例
 *     parameters:
 *       - name: ids
 *         description: id数组
 *         in: formData
 *         required: true
 *         type: string
 *     responses:
 *       200:
 *         description: status/data结构
 */
router.delete('/batch', async function (req, res, next) {
  await Instance.updateMany({relations: {$in: req.body.ids} }, { $pull: { relations: { $in: req.body.ids }}})
  await RelationInstance.deleteMany({_id: {$in: req.body.ids }})
  return res.json({ status: 'success'})
})

module.exports = router;
